function isNumeric(c) {
  return c >= "0" && c <= "9";
}

function isAlpha(c) {
  return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z");
}

// 将源码装换成 token
export function lexer(file, str) {
  let line = 1;
  let column = 1;
  let cursor = 0;
  let char = str[cursor];
  // console.log("lexer:", char);

  const position = () => {
    return { cursor, line, column };
  };

  const next = () => {
    cursor++;
    char = str[cursor];
    // console.log("lexer:", char);
    column++;
  };

  const newline = () => {
    line++;
    column = 1;
  };

  const number = () => {
    let buffer = "";
    const start = position();
    while (isNumeric(char)) {
      // TODO 符合的语法组合起来
      buffer += char;

      next();
    }

    if (buffer.length >= 1) {
      const end = position();
      return {
        type: "NumbericLiteral",
        value: Number(buffer),
        loc: {
          file,
          start,
          end,
        },
      };
    }

    return null;
  };

  // 关键字
  const keywords = {
    if: "If",
    else: "Else"
  };

  const id = () => {
    let buffer = "";

    if (!isAlpha(char)) return null;

    const start = position();
    buffer += char;
    next();

    while (isNumeric(char) || isAlpha(char)) {
      // TODO 符合的语法组合起来
      buffer += char;

      next();
    }

    const end = position();
    const type = keywords[buffer];
    if (type) {
      return {
        type, // 关键字之类的
        // value: buffer,
        loc: {
          file,
          start,
          end,
        },
      };
    }

    return {
      type: "Id", // 表示函数之类的
      value: buffer,
      loc: {
        file,
        start,
        end,
      },
    };
  };

  const regexp = () => {
    // 正则的斜杠 example: /foo/
    if (char === "/") {
      // "/"
      const start = position();
      next(); // "f"
      if (char === "/") {
        next();
        return readComment(start);
      }

      next();
      while (char !== "/") {
        next();
      }
      next(); // last /
      const end = position();
      return {
        type: "RegExpToken",
        // value: "/",
        loc: {
          file,
          start,
          end,
        },
      };
    }
  };

  const stringOfType = (delimiter) => {
    // 字符串 example: "124"
    if (char !== delimiter) return null;

    const start = position();
    next();
    while (char !== delimiter) {
      next();
    }
    next(); // last delimiter
    const end = position();
    return {
      type: "String",
      // value: "string",
      loc: {
        file,
        start,
        end,
      },
    };
  };

  function string() {
    return stringOfType('"') || stringOfType("'");
  }

  //#region // 注释
  const readComment = (start) => {
    for (; ;) {
      if (char === "\n") {
        newline();
        next();
        break;
      }

      if (char === undefined) {
        break;
      }

      next();
    }

    // while (char !== "\n" && char !== undefined) {
    //   next();
    //   newline();
    // }

    const end = position();

    return {
      type: "CommentToken",
      // value: "//", // 注释
      loc: {
        file,
        start,
        end,
      },
    };
  };
  //#endregion

  const operator = () => {
    if (char === "+") {
      const start = position();
      next();
      const end = position();
      return {
        type: "PlusToken",
        // value: "+",
        loc: {
          file,
          start,
          end,
        },
      };
    }
    if (char === "*") {
      const start = position();
      next();
      const end = position();
      return {
        type: "MulToken",
        // value: "*",
        loc: {
          file,
          start,
          end,
        },
      };
    }
    if (char === "/") {
      const start = position();
      next();
      if (char === "/") {
        next();
        return readComment(start);
      }
      const end = position();
      return {
        type: "DivToken",
        // value: "/",
        loc: {
          file,
          start,
          end,
        },
      };
    }

    return null;
  };

  // 分号
  const semicolon = () => {
    if (char !== ";") return null;

    const start = position();
    next();
    const end = position();

    return {
      type: "Semicolon",
      loc: {
        file,
        start,
        end,
      },
    };
  };

  const parents = () => {
    if (char === "(") {
      // 左括号
      const start = position();
      next();
      const end = position();
      return {
        type: "OpenParent",
        loc: {
          file,
          start,
          end,
        },
      };
    }

    if (char === ")") {
      // 右括号
      const start = position();
      next();
      const end = position();
      return {
        type: "CloseParent",
        loc: {
          file,
          start,
          end,
        },
      };
    }

    return null;
  };

  const isWhitespace = (c) => {
    return c === " " || c === "\t";
  };

  const whitespace = () => {
    const start = position();
    if (!isWhitespace(char)) {
      return null;
    }

    next();

    // while (isWhitespace(char)) {
    //   next();
    // }
    const end = position();

    return {
      type: "Whitespace",
      loc: {
        file,
        start,
        end,
      },
    };
  };

  const eol = () => {
    const start = position();

    if (char !== "\n") {
      return null;
    }

    next();
    newline();

    // while (char === '\n') {
    //   next();
    //   newline();
    // }
    const end = position();
    return {
      type: "Newline",
      loc: {
        file,
        start,
        end,
      },
    };
  };

  const eof = () => {
    if (char === undefined) {
      const start = position();
      const end = start;
      return {
        type: "EndOfFileToken",
        // value: undefined
        loc: {
          file,
          start,
          end,
        },
      };
    }
    return null;
  };

  const next2 = (mode) => {
    const value = () => {
      return number() || string() || regexp();
    };
    for (; ;) {
      // const token = mode === "expression"
      //   ? whitespace() || regexp() || number() || eol()
      //   : whitespace() || operator() || number() || eol();
      const token =
        whitespace() ||
        id() ||
        semicolon() ||
        parents() ||
        (mode === "expression" ? value() : operator()) ||
        eol();

      if (token) {
        // if (token === true) {
        //   continue;
        // }

        return token;
      }

      const maybeEof = eof();
      if (maybeEof) {
        return maybeEof;
      }

      // 找到不符合的语法
      // throw new SyntaxError(`Unexpected character "${char}" at ${cursor + 1}`);
      throw new SyntaxError(
        `Unexpected character "${char}" at ${file} ${line}:${column}`
      );
    }
  };

  return {
    next: next2,
  };
}
